"""
XCP协议实现模块
实现XCP协议的主机端（Master）逻辑
"""
import struct
import time
from typing import Tuple, Optional, List
from .transport_interface import ITransport, TransCanFrame
from .config import XcpCmdCode, XcpRspCode, XcpErrCode, XcpConfig


class XcpProtocol:
    """XCP协议主机端实现"""
    
    def __init__(self, transport: ITransport, config: XcpConfig):
        self._transport = transport
        self._config = config
        self._connected = False
        self._session_status = 0
        self._tx_id = 0x001
        self._rx_id = 0x000
        self._max_cto_len = config.max_cto_len
        self._max_dto_len = config.max_dto_len
        
        self._reboot_app_cmd = [0x00, 0x01]
        
    def set_can_ids(self, tx_id: int, rx_id: int) -> None:
        """设置CAN ID"""
        self._tx_id = tx_id
        self._rx_id = rx_id
        
    def connect(self, mode: int = 0) -> Tuple[bool, str]:
        """
        连接到XCP从机
        
        Args:
            mode: 连接模式
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            # 构造连接命令
            cmd_data = [XcpCmdCode.CONNECT, mode]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送连接命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送连接命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, f"接收连接响应失败: {msg}"
            
            # 解析响应
            if len(rsp_frame.data) < 2:
                return False, "响应数据长度不足"
            
            if rsp_frame.data[0] == XcpRspCode.OK:
                self._connected = True
                # 解析连接响应信息
                if len(rsp_frame.data) >= 8:
                    self._session_status = rsp_frame.data[1]
                    self._max_cto_len = rsp_frame.data[2]
                    self._max_dto_len = rsp_frame.data[3]
                    
                return True, f"XCP连接成功 - 会话状态: 0x{self._session_status:02X}"
            
            elif rsp_frame.data[0] == XcpRspCode.ERROR:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, f"XCP连接失败 - 错误码: 0x{error_code:02X}"
            
            else:
                return False, f"未知响应码: 0x{rsp_frame.data[0]:02X}"
                
        except Exception as e:
            return False, f"连接过程异常: {str(e)}"
        
        
    def disconnect(self) -> Tuple[bool, str]:
        """
        断开XCP连接
        """
        try:
            cmd_data = [XcpCmdCode.DISCONNECT]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送断开连接命令失败: {msg}"
            
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, f"接收断开连接响应失败: {msg}"
            
            if rsp_frame.data[0] == XcpRspCode.OK:
                self._connected = False
                return True, "XCP连接断开成功"
            else:
                return False, f"XCP断开连接失败 - 错误码: 0x{rsp_frame.data[0]:02X}"
                
        except Exception as e:
            return False, f"断开连接过程异常: {str(e)}"
    
    def get_status(self) -> Tuple[bool, Optional[int], str]:
        """
        获取XCP状态
        
        Returns:
            Tuple[bool, Optional[int], str]: (是否成功, 状态值, 消息)
        """
        try:
            if not self._connected:
                return False, None, "XCP未连接"
            
            # 构造获取状态命令
            cmd_data = [XcpCmdCode.GET_STATUS]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, None, f"发送获取状态命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, None, f"接收获取状态响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                status = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return True, status, f"获取状态成功 - 状态: 0x{status:02X}"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, None, f"获取状态失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, None, f"获取状态过程异常: {str(e)}"
            

    
    def set_mta(self, address: int) -> Tuple[bool, str]:
        """
        设置内存传输地址(MTA) - 用于设置擦除/编程的起始地址
        
        命令格式：
        - 字节0: SET_MTA命令码 (0xF6)
        - 字节1-3: 保留字节 (通常为0)
        - 字节4-7: 32位地址（小端序）
          * data[4] = 地址的低8位 (LSB)
          * data[5] = 地址的第二个8位
          * data[6] = 地址的第三个8位  
          * data[7] = 地址的高8位 (MSB)
        
        Args:
            address: 内存地址（从S19文件中解析得到的段起始地址）
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 构造设置MTA命令
            # 地址以小端序编码为4个字节：data[4-7]
            cmd_data = [XcpCmdCode.SET_MTA, 0, 0, 0] + list(struct.pack('<L', address))
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送设置MTA命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, f"接收设置MTA响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                return True, f"设置MTA成功 - 地址: 0x{address:08X}"
            else:
                return False, f"设置MTA失败 - 响应码: 0x{rsp_frame.data[0]:02X}"
                
        except Exception as e:
            return False, f"设置MTA过程异常: {str(e)}"
    
    def reboot_target_app(self) -> Tuple[bool, str]:
        """
        重启目标设备
        """
        try:
            # if not self._connected:
            #     return False, "XCP未连接"   
            
            # 构造重启命令
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=self._reboot_app_cmd,
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送重启命令失败: {msg}"
            return True, "重启目标设备成功"
        except Exception as e:
            return False, f"重启目标设备过程异常: {str(e)}"
        
        
    
    def upload(self, size: int) -> Tuple[bool, Optional[list[int]], str]:
        """
        从目标上传数据
        
        Args:
            size: 要上传的数据大小
            
        Returns:
            Tuple[bool, Optional[list[int]], str]: (是否成功, 上传的数据, 消息)
        """
        try:
            if not self._connected:
                return False, None, "XCP未连接"
            
            if size > (self._max_dto_len - 1):
                return False, None, f"数据长度超过限制: {size} > {self._max_dto_len - 1}"
            
            # 构造上传命令
            cmd_data = [XcpCmdCode.UPLOAD, size]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, None, f"发送上传命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, None, f"接收上传响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                uploaded_data = rsp_frame.data[1:1+size]
                return True, uploaded_data, f"上传数据成功 - 长度: {size} bytes"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, None, f"上传数据失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, None, f"上传过程异常: {str(e)}"
    
    def program_start(self) -> Tuple[bool, str]:
        """
        开始编程会话
        
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 构造编程开始命令
            cmd_data = [XcpCmdCode.PROGRAM_START]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送编程开始命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t3)
            if not suc or rsp_frame is None:
                return False, f"接收编程开始响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                return True, "编程会话开始成功"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, f"编程会话开始失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, f"编程开始过程异常: {str(e)}"
    
    def program(self, data: list[int], data_len:int) -> Tuple[bool, str]:
        """
        编程数据到目标
        
        命令格式（根据MCU端XcpCmdProgramMax函数）：
        - 字节0: PROGRAM命令码 (0xD0) 
        - 字节1-7: 要编程的数据（最多4字节）
        
        Args:
            data: 要编程的数据（字节列表）
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 限制数据长度为7字节（data[1-7]）
            if len(data) > 7:
                return False, f"数据长度超过限制: {len(data)} > 7"
            
            # 构造编程命令：命令码 + 数据
            # MCU端期望: data[0]=命令码, data[1-7]=编程数据
            cmd_data = [XcpCmdCode.PROGRAM, data_len] + data
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送编程命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, f"接收编程响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                return True, f"编程数据成功 - 长度: {len(data)} bytes"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, f"编程数据失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, f"编程过程异常: {str(e)}"
        
    def program_max(self, data: list[int]) -> Tuple[bool, str]:
        """
        编程数据到目标（使用PROGRAM_MAX命令）
        
        命令格式（根据MCU端XcpCmdProgramMax函数）：
        - 字节0: PROGRAM_MAX命令码 (0xC9) 
        - 字节1-7: 要编程的数据（最多7字节）
        
        Args:
            data: 要编程的数据（字节列表）
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 限制数据长度为7字节（data[1-7]）
            if len(data) > 7:
                return False, f"数据长度超过限制: {len(data)} > 7"
            
            # 构造编程命令：使用PROGRAM_MAX命令码 + 数据
            # MCU端期望: data[0]=PROGRAM_MAX命令码, data[1-7]=编程数据
            cmd_data = [XcpCmdCode.PROGRAM_MAX] + data
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # return True, "ok"
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送编程命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, f"接收编程响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                return True, f"编程数据成功 - 长度: {len(data)} bytes"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, f"编程数据失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, f"编程过程异常: {str(e)}"
    
    def program_clear(self, size: int) -> Tuple[bool, str]:
        """
        清除程序存储器
        
        命令格式：
        - 字节0: PROGRAM_CLEAR命令码 (0xD1)
        - 字节1-3: 保留字节 (通常为0)
        - 字节4-7: 擦除长度（小端序，32位）
          * data[4] = 长度的低8位 (LSB)
          * data[5] = 长度的第二个8位
          * data[6] = 长度的第三个8位
          * data[7] = 长度的高8位 (MSB)
        
        Args:
            size: 要清除的字节数
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 构造清除程序命令
            # 在8字节CAN帧中，使用小端序将32位长度值编码为最后4个字节：data[4-7]
            # 这与目标端的解析逻辑匹配：
            # result |= (blt_int32u)data[4];           // LSB  
            # result |= (blt_int32u)((blt_int32u)data[5] << 8);
            # result |= (blt_int32u)((blt_int32u)data[6] << 16);
            # result |= (blt_int32u)((blt_int32u)data[7] << 24); // MSB
            cmd_data = [XcpCmdCode.PROGRAM_CLEAR, 0, 0, 0] + list(struct.pack('<L', size))
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送清除程序命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t4)
            if not suc or rsp_frame is None:
                return False, f"接收清除程序响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                return True, f"清除程序存储器成功 - 大小: {size} bytes"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, f"清除程序存储器失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, f"清除程序过程异常: {str(e)}"
    
    def program_reset(self) -> Tuple[bool, str]:
        """
        复位程序
        
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._connected:
                return False, "XCP未连接"
            
            # 构造复位程序命令
            cmd_data = [XcpCmdCode.PROGRAM_RESET]
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, f"发送复位程序命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t5)
            
            return True, ""
            # if not suc or rsp_frame is None:
            #     return False, f"接收复位程序响应失败: {msg}"
            
            # # 解析响应
            # if rsp_frame.data[0] == XcpRspCode.OK:
            #     self._connected = False  # 复位后连接断开
            #     return True, "程序复位成功"
            # else:
            #     error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
            #     return False, f"程序复位失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, f"程序复位过程异常: {str(e)}"
    
    
    
    def is_connected(self) -> bool:
        """检查是否已连接"""
        return self._connected
    
    def get_session_status(self) -> int:
        """获取会话状态"""
        return self._session_status
    
    def build_checksum(self, length: int) -> Tuple[bool, Optional[int], str]:
        """
        发送BUILD_CHECKSUM命令获取MCU计算的校验和
        
        命令格式：
        - 字节0: BUILD_CHECKSUM命令码 (0xF3)
        - 字节1-3: 保留字节 (通常为0)
        - 字节4-7: 校验和长度（小端序，32位）
        
        响应格式：
        - 字节0: 响应PID (0xFF)
        - 字节1: checksum类型
        - 字节2-3: 保留字节
        - 字节4-7: checksum值（小端序，32位）
        
        Args:
            length: 要计算校验和的数据长度
            
        Returns:
            Tuple[bool, Optional[int], str]: (是否成功, 校验和值, 消息)
        """
        try:
            if not self._connected:
                return False, None, "XCP未连接"
            
            # 构造BUILD_CHECKSUM命令
            # 长度以小端序编码为4个字节：data[4-7]
            cmd_data = [XcpCmdCode.BUILD_CHECKSUM, 0, 0, 0] + list(struct.pack('<L', length))
            cmd_frame = TransCanFrame(
                can_id=self._tx_id,
                data=cmd_data + [0] * (8 - len(cmd_data)),
                is_extended=False
            )
            
            # 发送命令
            suc, msg = self._transport.send_frame(cmd_frame)
            if not suc:
                return False, None, f"发送BUILD_CHECKSUM命令失败: {msg}"
            
            # 接收响应
            suc, rsp_frame, msg = self._transport.recv_frame(self._config.timeout_t1)
            if not suc or rsp_frame is None:
                return False, None, f"接收BUILD_CHECKSUM响应失败: {msg}"
            
            # 解析响应
            if rsp_frame.data[0] == XcpRspCode.OK:
                # 解析checksum值（小端序，字节4-7）
                if len(rsp_frame.data) >= 8:
                    checksum_bytes = rsp_frame.data[4:8]
                    checksum_value = struct.unpack('<L', bytes(checksum_bytes))[0]
                    checksum_type = rsp_frame.data[1]
                    return True, checksum_value, f"BUILD_CHECKSUM成功 - 类型: 0x{checksum_type:02X}, 值: 0x{checksum_value:08X}"
                else:
                    return False, None, "BUILD_CHECKSUM响应数据长度不足"
            else:
                error_code = rsp_frame.data[1] if len(rsp_frame.data) > 1 else 0
                return False, None, f"BUILD_CHECKSUM失败 - 错误码: 0x{error_code:02X}"
                
        except Exception as e:
            return False, None, f"BUILD_CHECKSUM过程异常: {str(e)}" 